/*-------------------------------------------------------------------------
 *
 * fe-gssapi-common.c
 *     前端（客户端）GSSAPI公共代码
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *      src/interfaces/libpq/fe-gssapi-common.c
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include "fe-gssapi-common.h"

#include "libpq-int.h"
#include "pqexpbuffer.h"

/*
 * 获取特定类型的所有错误并附加到“str”中。
 * 每个错误字符串前面都有一个空格。
 */
static void fc_pg_GSS_error_int(PQExpBuffer fc_str, OM_uint32 fc_stat, int fc_type)
{
	OM_uint32	fc_lmin_s;
	gss_buffer_desc fc_lmsg;
	OM_uint32	fc_msg_ctx = 0;

	do
	{
		if (gss_display_status(&fc_lmin_s, fc_stat, fc_type, GSS_C_NO_OID,
							   &fc_msg_ctx, &fc_lmsg) != GSS_S_COMPLETE)
			break;
		appendPQExpBufferChar(fc_str, ' ');
		appendBinaryPQExpBuffer(fc_str, fc_lmsg.value, fc_lmsg.length);
		gss_release_buffer(&fc_lmin_s, &fc_lmsg);
	} while (fc_msg_ctx);
}

/*
 * GSSAPI 错误包含两个部分；将两者放入 conn->errorMessage 中。
 */
void pg_GSS_error(const char *fc_mprefix, PGconn *fc_conn,
			 OM_uint32 fc_maj_stat, OM_uint32 fc_min_stat)
{
	appendPQExpBuffer(&fc_conn->errorMessage, "%s:", fc_mprefix);
	fc_pg_GSS_error_int(&fc_conn->errorMessage, fc_maj_stat, GSS_C_GSS_CODE);
	appendPQExpBufferChar(&fc_conn->errorMessage, ':');
	fc_pg_GSS_error_int(&fc_conn->errorMessage, fc_min_stat, GSS_C_MECH_CODE);
	appendPQExpBufferChar(&fc_conn->errorMessage, '\n');
}

/*
 * 检查我们是否可以获取凭证（如果可以，则返回它们）。
 */
bool pg_GSS_have_cred_cache(gss_cred_id_t *fc_cred_out)
{
	OM_uint32	fc_major,
				fc_minor;
	gss_cred_id_t fc_cred = GSS_C_NO_CREDENTIAL;

	fc_major = gss_acquire_cred(&fc_minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
							 GSS_C_INITIATE, &fc_cred, NULL, NULL);
	if (fc_major != GSS_S_COMPLETE)
	{
		*fc_cred_out = NULL;
		return false;
	}
	*fc_cred_out = fc_cred;
	return true;
}

/*
 * 尝试为连接加载服务名称
 */
int pg_GSS_load_servicename(PGconn *fc_conn)
{
	OM_uint32	fc_maj_stat,
				fc_min_stat;
	int			fc_maxlen;
	gss_buffer_desc fc_temp_gbuf;
	char	   *fc_host;

	if (fc_conn->gtarg_nam != NULL)
		/* 已经处理过 - 继续前进 */
		return STATUS_OK;

	fc_host = PQhost(fc_conn);
	if (!(fc_host && fc_host[0] != '\0'))
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("host name must be specified\n"));
		return STATUS_ERROR;
	}

	/*
	 * 导入服务主体名称，以便 GSSAPI 系统能够获取正确的票证。
	 */
	fc_maxlen = strlen(fc_conn->krbsrvname) + strlen(fc_host) + 2;
	fc_temp_gbuf.value = (char *) malloc(fc_maxlen);
	if (!fc_temp_gbuf.value)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return STATUS_ERROR;
	}
	snprintf(fc_temp_gbuf.value, fc_maxlen, "%s@%s",
			 fc_conn->krbsrvname, fc_host);
	fc_temp_gbuf.length = strlen(fc_temp_gbuf.value);

	fc_maj_stat = gss_import_name(&fc_min_stat, &fc_temp_gbuf,
							   GSS_C_NT_HOSTBASED_SERVICE, &fc_conn->gtarg_nam);
	free(fc_temp_gbuf.value);

	if (fc_maj_stat != GSS_S_COMPLETE)
	{
		pg_GSS_error(libpq_gettext("GSSAPI name import error"),
					 fc_conn,
					 fc_maj_stat, fc_min_stat);
		return STATUS_ERROR;
	}
	return STATUS_OK;
}
